本文是「測試之外的那一半:一個 QA 回頭帶當年的自己」系列第 22 篇。
實習的時候我最常講的一句話是「這版怪怪的」。
畫面一片空白、測試找不到元素、跑一跑就當掉——我會很自然地說出「這版有問題」,然後開單。
現在我知道那句話幾乎沒有資訊量。而且它常常是誣賴。
有一個列表很重、畫面繪製吃資源的 App。在模擬器上打開之後整片黑,大約二十秒跳出「應用程式無回應」。
直覺結論很清楚:這版安裝檔壞了。
但一條條看 log,事實完全相反。
後端 API 回 200,資料正常送達,所以不是網路或後端問題。沒有 FATAL EXCEPTION、沒有 crash 堆疊,所以程式碼沒有真的崩潰。記憶體充足,所以不是 OOM。
唯一異常的是一堆 GPU 繪製相關的錯誤,EGL_BAD_MATCH 那一類。
拼起來:後端正常、沒有例外、不是記憶體不足、只有繪製層報錯。
這不是 APK 的問題,是模擬器的 GPU 撐不住這個畫面。換一台真手機跑,一切正常。
實習的我看到黑畫面,會直接開單:「開啟後黑畫面,約 20 秒後 ANR」。
那張單沒有錯,但它會送 RD 去查一個沒有壞的東西。他查完會回「我這邊正常」,然後單子在我們之間來回幾次,最後大概會被標成無法重現。
差別不在我當年不夠細心。差別在我當時沒有一套分層可以照著問。我只有一個層級:App。所以所有現象都只能歸因到那一層。
| 層 | 要問的問題 |
|---|---|
| 後端/資料層 | API 回什麼?資料有沒有送到? |
| App 邏輯層 | 有沒有 FATAL EXCEPTION、crash 堆疊?行程還活著嗎? |
| 資源/環境層 | 是不是 OOM?有沒有 GPU 或繪製錯誤?換一台重現嗎? |
| 覆蓋層 | 畫面上是不是有別的東西蓋住了?推播、系統對話框、外部網頁 |
這四層的順序不是隨便排的。它是照「排除成本由低到高」排的——看 API 回應碼最快,抓 crash log 次之,換機器要花點時間,而覆蓋層要你去看當下的截圖而不是只看錯誤訊息。
最有效的一招其實是最土的那個:換一台跑跑看,尤其換到真手機。 環境變因一換,環境問題當場現形。
我一開始以為這套分層的用處是「證明不是 App 的錯」。
後來發現不是。它真正的用處是讓你交出去的東西從結論變回觀察加證據。
「App 壞了」是結論。「畫面黑了」是觀察。從觀察到結論之間隔著好幾層可能性,而分層做的事,是把那幾層攤開來,讓看單子的人知道你排除到哪裡了。
這跟 Day 10 講的關單要寫三行是同一件事,只是換到開單那一端:我看到什麼、我排除了什麼、我還沒排除什麼。
上面那張表的最後兩層,各有一個我踩過的完整案例,值得單獨講。
覆蓋層:有個測試只在「連續跑一整輪」的時候偶爾失敗,單獨跑永遠不重現。那個「偶爾」不是隨機,它在告訴我一件事。明天講。
環境層:同一份腳本,我筆電上跑得完登入,搬到共用機器就卡住,而且兩邊腳本一字不差。後天講。
開單之前,把你要寫的第一句話念一遍,然後問:
這是我看到的,還是我推論出來的?
「畫面黑了」是看到的。「這版 APK 壞了」是推論出來的。
兩句都可以寫,但順序不能反。先寫觀察,再寫你排除過哪幾層、剩下哪一層最可疑。
少了中間那段,你交出去的不是一份診斷,是一個要別人幫你重做一次的猜測。
明天聊:測試偶爾失敗的時候,那個「偶爾」在說什麼。